package logica;

import io.Archivo;
import io.Carpeta;
import io.Cliente;
import io.ItemAgenda;
import io.ItemBusqueda;
import io.hd.ConfiguracionIO;
import io.hd.GestorArchivo;
import io.hd.GestorArchivoListener;
import io.red.GestorInterfacesRed;
import io.servicios.ActualizadorListener;
import io.servicios.Conexion;
import io.servicios.ConexionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;

/**
 *
 * @author Mariano Zapata
 */
public class Nucleo implements ActualizadorListener, GestorArchivoListener, ConexionListener {

    private Conexion conexion;
    private GestorArchivo gestorArchivos;
    private ActualizacionArbolListener listenerArbol;
    private ActualizacionCalendarioListener listenerCalendario;
    private InformarUsuarioListener listenerUsuario;
    private GestorInterfacesRed gir;
    
    private static final String PATH_CONFIGURACION_RED= "conf_red.properties";
    private static final String PATH_CONFIGURACION_APPS= "conf_apps.properties";
    private static final String KEY_IP= "ip";
    private static final String KEY_PUERTO_RMI_REMOTO= "puertoRMI_remoto";
    private static final String KEY_PUERTO_RMI_LOCAL= "puertoRMI_local";
    private static final String KEY_INTERFACE_RED= "nombreInterfaceRed";
    private static final String KEY_TIPO_IP= "tipoIP";
    private static final String VERSION_SISTEMA= "2.0";
    private static final String FECHA_COMPILACION= "01/01/2012";
    public static final String ERROR_SIN_EXTENSION= "El archivo no posee una extensión.";
    public static final String ERROR_ARCHIVO_DESCONOCIDO= "Tipo de archivo desconocido.\nSe debe configurar " +
            "la aplicación externa a utilizar.";
    public static final String ERROR_APLICACION_EXTERNA= "Ha ocurrido un error al intentar ejecutar la aplicación\n" +
            "externa asociada a este archivo.\n\nDetalle: ";
    
    public Nucleo() throws RemoteException
    {
        conexion= new Conexion();
        gir= new GestorInterfacesRed();
        gestorArchivos= new GestorArchivo(conexion.getDirectorioTemp());
        listenerArbol= null;
        listenerCalendario= null;
        listenerUsuario= null;
    }
    
    public void conectar() throws RemoteException, NotBoundException, UnknownHostException, IOException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_RED);
        String ipServidor= conf.getProperty(KEY_IP);
        int puertoRMIRemoto= Integer.parseInt(conf.getProperty(KEY_PUERTO_RMI_REMOTO));
        int puertoRMILocal= Integer.parseInt(conf.getProperty(KEY_PUERTO_RMI_LOCAL));
        String interfaceRed= conf.getProperty(KEY_INTERFACE_RED);
        int tipoIP= Integer.parseInt(conf.getProperty(KEY_TIPO_IP));
        gir.configurar(interfaceRed, tipoIP);
        conexion.agregarActualizadorListener(this);
        conexion.agregarConexionListener(this);
        conexion.conectarServicio(ipServidor, puertoRMIRemoto, puertoRMILocal, gir.getInetAddress());
        gestorArchivos.agregarGestorArchivoListener(this);
        gestorArchivos.start();
    }
    
    /**
     * Utilizar este método cuando la aplicación se cierra de forma normal, es decir, no hay clientes ni archivos abiertos.
     */
    public void desconectar()
    {
        conexion.quitarActualizadorListener();
        gestorArchivos.quitarGestorArchivoListener();
        gestorArchivos.detener();
        conexion.desconectarServicio();
    }
    
    /**
     * Utilizar este método cuando la aplicación debe cerrarse y no pueden ser guardados los clientes ni archivos.
     */
    public void desconectarPorErrorGrave()
    {
        if (gestorArchivos != null)
        {
            gestorArchivos.quitarGestorArchivoListener();
            gestorArchivos.detener();
        }
        if (conexion != null)
        {
            conexion.quitarActualizadorListener();
            conexion.desconectarServicio();
        }
    }
    
    public static String obtenerVersionJVM() {
        return System.getProperty("java.version");
    }
    
    public static String obtenerVersionSistema() {
        return VERSION_SISTEMA;
    }
    
    public static String obtenerFechaCompilacion() {
        return FECHA_COMPILACION;
    }
    
    public List<String> obtenerInterfacesRed() throws SocketException {
        return gir.getInterfacesRed();
    }
    
    public String [] obtenerConfiguracionRed() throws IOException
    {
        String [] retorno= new String[5];
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_RED);
        retorno[0]= conf.getProperty(KEY_IP);
        retorno[1]= conf.getProperty(KEY_PUERTO_RMI_REMOTO);
        retorno[2]= conf.getProperty(KEY_PUERTO_RMI_LOCAL);
        retorno[3]= conf.getProperty(KEY_INTERFACE_RED);
        retorno[4]= conf.getProperty(KEY_TIPO_IP);
        return retorno;
    }
    
    public void guardarConfiguracionRed(String ip, String puertoRMIRemoto, String puertoRMILocal, String interfaceRed,
            int tipoIP) throws IOException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_RED);
        conf.setProperty(KEY_IP, ip);
        conf.setProperty(KEY_PUERTO_RMI_REMOTO, puertoRMIRemoto);
        conf.setProperty(KEY_PUERTO_RMI_LOCAL, puertoRMILocal);
        conf.setProperty(KEY_INTERFACE_RED, interfaceRed);
        conf.setProperty(KEY_TIPO_IP, String.valueOf(tipoIP));
        ConfiguracionIO.guardarConfiguracion(conf, PATH_CONFIGURACION_RED);
    }
    
    public List<String []> obtenerConfiguracionApps() throws IOException
    {
        ArrayList<String []> retorno= new ArrayList<String []>();
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_APPS);
        Set<String> claves= conf.stringPropertyNames();
        for (String claveActual : claves)
        {
            String [] par= new String[2];
            par[0]= claveActual;
            par[1]= conf.getProperty(claveActual);
            retorno.add(par);
        }
        return retorno;
    }
    
    public void guardarConfiguracionApps(List<String []> apps) throws IOException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_APPS);
        conf.clear();
        for (String [] par : apps)
            conf.setProperty(par[0], par[1]);
        ConfiguracionIO.guardarConfiguracion(conf, PATH_CONFIGURACION_APPS);
    }
    
    public void setSistemaOcupado() {
        listenerUsuario.cursorOcupado();
    }
    
    public void setSistemaLibre() {
        listenerUsuario.cursorLibre();
    }
    
    public void agregarActualizacionArbolListener(ActualizacionArbolListener aal) {
        listenerArbol= aal;
    }
    
    public void quitarActualizacionArbolListener() {
        listenerArbol= null;
    }
    
    public void agregarActualizacionCalendarioListener(ActualizacionCalendarioListener acl) {
        listenerCalendario= acl;
    }
    
    public void quitarActualizacionCalendarioListener() {
        listenerCalendario= null;
    }
    
    public void agregarInformarUsuarioListener(InformarUsuarioListener iul) {
        listenerUsuario= iul;
    }
    
    public void quitarInformarUsuarioListener() {
        listenerUsuario= null;
    }
    
    public Carpeta expandirCarpeta(Carpeta aExpandir) throws RemoteException {
        return conexion.getServiciosServidor().expandirCarpeta(aExpandir);
    }

    public Carpeta abrirExpediente(Carpeta expedienteAbrir) throws RemoteException {
        return conexion.getServiciosServidor().abrirExpediente(expedienteAbrir, conexion.getIdConexion());
    }

    public void cerrarExpediente(Carpeta expedienteCerrar) throws RemoteException {
        conexion.getServiciosServidor().cerrarExpediente(expedienteCerrar);
    }
    
    public Cliente abrirCliente(Cliente aAbrir) throws RemoteException {
        return conexion.getServiciosServidor().abrirCliente(aAbrir, conexion.getIdConexion());
    }
    
    public void cerrarCliente(Cliente aCerrar) throws RemoteException {
        conexion.getServiciosServidor().cerrarCliente(aCerrar);
    }
    
    public Object [] crearCliente(Cliente aCrear, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().crearCliente(aCrear, padre, conexion.getIdConexion());
    }
    
    public Carpeta guardarCliente(Cliente aGuardar) throws RemoteException {
        return conexion.getServiciosServidor().guardarCliente(aGuardar, conexion.getIdConexion());
    }
    
    public boolean eliminarCliente(Cliente aEliminar, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().eliminarCliente(aEliminar, padre, conexion.getIdConexion());
    }
    
    public Carpeta cortarCliente(Cliente aCortar, Carpeta padreNuevo, Carpeta padreViejo) throws RemoteException {
        return conexion.getServiciosServidor().cortarCliente(aCortar, padreNuevo, padreViejo, conexion.getIdConexion());
    }
    
    public Carpeta copiarCliente(Cliente aCopiar, Carpeta padreNuevo) throws RemoteException {
        return conexion.getServiciosServidor().copiarCliente(aCopiar, padreNuevo, conexion.getIdConexion());
    }
    
    public Object [] crearCarpeta(Carpeta aCrear, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().crearCarpeta(aCrear, padre, conexion.getIdConexion());
    }
    
    public Carpeta guardarCarpeta(Carpeta aGuardar) throws RemoteException {
        return conexion.getServiciosServidor().guardarCarpeta(aGuardar, conexion.getIdConexion());
    }
    
    public List<Cliente> eliminarCarpeta(Carpeta aEliminar, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().eliminarCarpeta(aEliminar, padre, conexion.getIdConexion());
    }
    
    public Carpeta cortarCarpeta(Carpeta aCortar, Carpeta padreNuevo, Carpeta padreViejo) throws RemoteException {
        return conexion.getServiciosServidor().cortarCarpeta(aCortar, padreNuevo, padreViejo, conexion.getIdConexion());
    }
    
    public Carpeta copiarCarpeta(Carpeta aCopiar, Carpeta padreNuevo) throws RemoteException {
        return conexion.getServiciosServidor().copiarCarpeta(aCopiar, padreNuevo, conexion.getIdConexion());
    }
    
    public Carpeta crearArchivo(File aCrear, Carpeta padre) throws RemoteException, FileNotFoundException, IOException
    {
        Archivo archivo= new Archivo(new ArrayList<Long>());
        archivo.setNombre(aCrear.getName());
        String nombreArchivoTempServidor= conexion.getServiciosServidor().crearArchivoTemporal();
        gestorArchivos.escribirArchivoEnServidor(aCrear, conexion, nombreArchivoTempServidor);
        Carpeta ret= conexion.getServiciosServidor().crearArchivo(archivo, padre, conexion.getIdConexion(),
                nombreArchivoTempServidor);
        aCrear.delete();
        return ret;
    }
    
    public void abrirArchivo(Archivo aAbrir) throws RemoteException, IOException, AbrirArchivoException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION_APPS);
        String extension= aAbrir.getExtension();
        if (extension == null)
            throw new AbrirArchivoException(ERROR_SIN_EXTENSION);
        String pathApp= conf.getProperty(extension);
        if (pathApp == null)
            throw new AbrirArchivoException(ERROR_ARCHIVO_DESCONOCIDO);
        conexion.getServiciosServidor().abrirArchivo(aAbrir, conexion.getIdConexion());
        try {
            gestorArchivos.abrirArchivo(aAbrir, pathApp);
        } catch (IOException ex) {this.cerrarArchivo(aAbrir, false);
                                  throw new AbrirArchivoException(ERROR_APLICACION_EXTERNA + ex.getMessage());}
    }
    
    public Carpeta cambiarNombreArchivo(Archivo aCambiar, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().cambiarNombreArchivo(aCambiar, padre, conexion.getIdConexion());
    }
    
    public boolean cerrarArchivo(Archivo aCerrar, boolean guardar) throws RemoteException, FileNotFoundException,
            IOException
    {
        String nombreArchivoTempServidor= "";
        if (guardar)
            nombreArchivoTempServidor= conexion.getServiciosServidor().crearArchivoTemporal();
        if (gestorArchivos.cerrarArchivo(aCerrar, guardar, conexion, nombreArchivoTempServidor))
        {
            conexion.getServiciosServidor().cerrarArchivo(aCerrar, guardar, nombreArchivoTempServidor);
            return true;
        }
        else
            return false;
    }
    
    public boolean eliminarArchivo(Archivo aEliminar, Carpeta padre) throws RemoteException {
        return conexion.getServiciosServidor().eliminarArchivo(aEliminar, padre, conexion.getIdConexion());
    }
    
    public Carpeta cortarArchivo(Archivo aCortar, Carpeta padreNuevo, Carpeta padreViejo) throws RemoteException {
        return conexion.getServiciosServidor().cortarArchivo(aCortar, padreNuevo, padreViejo, conexion.getIdConexion());
    }

    public Carpeta copiarArchivo(Archivo aCopiar, Carpeta padreNuevo) throws RemoteException {
        return conexion.getServiciosServidor().copiarArchivo(aCopiar, padreNuevo, conexion.getIdConexion());
    }
    
    public boolean archivoAbierto(Archivo ar) {
        return gestorArchivos.estaAbierto(ar);
    }
    
    public List<ItemAgenda> obtenerItemsAgenda(String fecha) throws RemoteException {
        return conexion.getServiciosServidor().obtenerItemsAgenda(fecha);
    }
    
    public List<ItemAgenda> crearItemAgenda(ItemAgenda aCrear) throws RemoteException {
        return conexion.getServiciosServidor().crearItemAgenda(aCrear, conexion.getIdConexion());
    }
    
    public boolean eliminarItemAgenda(ItemAgenda aEliminar) throws RemoteException {
        return conexion.getServiciosServidor().eliminarItemAgenda(aEliminar, conexion.getIdConexion());
    }
    
    public List<ItemAgenda> modificarItemAgenda(ItemAgenda aModificar) throws RemoteException {
        return conexion.getServiciosServidor().modificarItemAgenda(aModificar, conexion.getIdConexion());
    }

    public List<Integer> obtenerDiasConEventosImportantes(String fechaInicio, String fechaFin) throws RemoteException {
        return conexion.getServiciosServidor().obtenerDiasConEventosImportantes(fechaInicio, fechaFin);
    }
    
    public List<ItemBusqueda> buscarCarpeta(String palabraClave, boolean concordanciaInicio) throws RemoteException {
        return conexion.getServiciosServidor().buscarCarpeta(palabraClave, concordanciaInicio);
    }
    
    public List<ItemBusqueda> buscarArchivo(String palabraClave, boolean concordanciaInicio) throws RemoteException {
        return conexion.getServiciosServidor().buscarArchivo(palabraClave, concordanciaInicio);
    }
    
    public List<ItemBusqueda> buscarClienteApellido(String palabraClave, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarClienteApellido(palabraClave, concordanciaInicio);
    }
    
    public List<ItemBusqueda> buscarClienteNombre(String palabraClave, boolean concordanciaInicio) throws RemoteException {
        return conexion.getServiciosServidor().buscarClienteNombre(palabraClave, concordanciaInicio);
    }
    
    public List<ItemBusqueda> buscarClienteDni(String palabraClave) throws RemoteException {
        return conexion.getServiciosServidor().buscarClienteDni(palabraClave);
    }

    public List<ItemBusqueda> buscarExpedienteCaratula(String palabraClave, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteCaratula(palabraClave, concordanciaInicio);
    }

    public List<ItemBusqueda> buscarExpedienteInstancia(String palabraClave, int nroInstancia, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteInstancia(palabraClave, nroInstancia, concordanciaInicio);
    }

    public List<ItemBusqueda> buscarExpedienteJuzgado(String palabraClave, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteJuzgado(palabraClave, concordanciaInicio);
    }

    public List<ItemBusqueda> buscarExpedienteSecretaria(String palabraClave, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteSecretaria(palabraClave, concordanciaInicio);
    }

    public List<ItemBusqueda> buscarExpedienteTipo(String palabraClave, boolean concordanciaInicio)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteTipo(palabraClave, concordanciaInicio);
    }

    public List<ItemBusqueda> buscarExpedienteEstado(byte estado)
            throws RemoteException {
        return conexion.getServiciosServidor().buscarExpedienteEstado(estado);
    }

    @Override
    public void actualizarCarpeta(Carpeta c, int operacion) {
        if (listenerArbol != null)
            listenerArbol.actualizarCarpeta(c, operacion);
    }
    
    @Override
    public void cortarObjeto(Object o, Carpeta vieja, Carpeta nueva) {
        if (listenerArbol != null)
            listenerArbol.cortarObjeto(o, vieja, nueva);
    }

    @Override
    public void quitarDeCarpeta(Carpeta padre, Object o) {
        if (listenerArbol != null)
            listenerArbol.quitarDeCarpeta(padre, o);
    }
    
    @Override
    public void archivoModificado(Archivo a) {
        if (listenerArbol != null)
            listenerArbol.archivoModificado(a);
    }

    @Override
    public void actualizarListaAgenda(List<ItemAgenda> lista) {
        if (listenerCalendario != null)
            listenerCalendario.actualizarListaAgenda(lista);
    }

    @Override
    public void quitarDeListaAgenda(ItemAgenda item) {
        if (listenerCalendario != null)
            listenerCalendario.quitarDeListaAgenda(item);
    }
    
    @Override
    public void setProximosEventos(List<ItemAgenda> eventos) {
        if (listenerUsuario != null)
            listenerUsuario.setProximosEventos(eventos);
    }
    
    @Override
    public void tiempoCumplido() {
        if (listenerUsuario != null)
            listenerUsuario.tiempoCumplido();
    }

    @Override
    public void conexionPerdida() {
        if (listenerUsuario != null)
            listenerUsuario.conexionPerdida();
    }
}